home *** CD-ROM | disk | FTP | other *** search
- ; WAITASEC.ASM -- Holds and Recalls Scrolling Screens
- ; ============
- ; (C) Copyright Charles Petzold, 1985
- ;
- ; COM file format
- ;
-
- CSEG Segment
- Assume CS:CSEG
- Org 0080h
- SWAP Label Byte ; Use this area for line swapping
- Org 0100h
- Entry: Jmp Initialize ; Entry: Jump over resident part
-
- ; All Data
- ; --------
-
- Cols equ 80 ; You may want to set this to 40
- ScrollLock equ 70 ; Scan code of Scroll Lock key
- NumberOfScreens dw 8 ; Can be set up to 15 for 80-column
-
- db '(C) Copyright Charles Petzold, 1985'
-
- DisplaySegment dw ? ; Display address set by VideoCheck
- OldInterrupt9 dd ? ; Saved address of real Interrupt 9
- OldInterrupt10 dd ? ; Ditto for Interrupt 10h
-
- TopHoldAddr dw Offset HoldArea ; Address of top of Hold area
- BotHoldAddr dw ? ; Address of bottom of Hold area
- SavePointer dw Offset HoldArea ; Points to where text is saved
- RecallPointer dw Offset HoldArea ; Points to where text is recalled
-
- LockStateOn db 0 ; Flag for lock or no lock
- ScrollLockDown db 0 ; Current state of Scroll Lock key
-
- KeyDispatch dw Home,Up,PgUp ; Dispatch addresss of routines
- dw 5 Dup (Dummy) ; for cursor movement within
- dw End,Down,PgDn ; recalled displays
-
- ; Intercept of Interrupt 10h -- BIOS Video Call
- ; --------------------------
-
- NewInterrupt10: Cmp AX,0601h ; Check if this is a 1 line scroll up
- Jne NoNeedToBother ; No, just continue to real interrupt
- Cmp CX,0 ; Check if this is upper left corner
- Jne NoNeedToBother ; No, don't want to touch it
- Cmp DL,Cols - 1 ; Check if DL is right hand side
- Jne NoNeedToBother ; Another obstacle that shuts us out
- Call VideoCheck ; Returns Carry if graphics
- Jc NoNeedToBother ; So we drop out here if Carry is set
-
- Sti ; Allow interrupts
- Cld ; And set direction to forward
- Push CX ; Save all the registers we'll use
- Push SI
- Push DI
- Push DS
- Push ES
-
- Push CS ; Push this code segment
- Pop ES ; So we can set ES to it
- Mov DI,CS:[SavePointer] ; This is the destination
- Mov DS,CS:[DisplaySegment] ; This is the source segment
- Sub SI,SI ; Source is top of display
- Mov CX,Cols ; The number of characters
-
- Call VideoOff ; Turn off light show if color
- Rep Movsw ; Move 'em in!
- Call VideoOn ; Turn display back on
-
- Cmp DI,CS:[BotHoldAddr] ; Check the new destination
- Jb NotAtBotYet ; Jump if it hasn't wrapped yet
- Mov DI,CS:[TopHoldAddr] ; Set it to top if it has
- NotAtBotYet: Mov CS:[SavePointer],DI ; And save it anyway
-
- Pop ES ; Pop the registers we saved
- Pop DS
- Pop DI
- Pop SI
- Pop CX
-
- NoNeedToBother: Jmp CS:[OldInterrupt10] ; And do the interrupt for real
-
- ; Intercept of Interrupt 9h -- Hardware Keyboard Interrupt
- ; --------------------------------------------------------
-
- NewInterrupt9: Sti ; Allow other interrupts
- Push AX ; Save the register for awhile
- In AL,60h ; Get the pressed key scan code
- Cmp AL,ScrollLock ; Check if it's the Scroll Lock
- Jz GottaScrollLock ; If so, do special routine
- Cmp AL,128 + ScrollLock ; Check if scroll lock release
- Jz ScrollLockRlse ; Another special routine
- Jmp AnyOtherKey ; Otherwise, it's some other key
-
- ; Scroll Lock key depressed
- ; -------------------------
-
- GottaScrollLock:Mov CS:[ScrollLockDown],1 ; Flag Scroll Lock as depressed
- Cmp CS:[LockStateOn],1 ; See if we're locked now
- Jz AnyOtherKey ; If so, will just reset
-
- Mov AH,2 ; Do keyboard interrupt
- Int 16h ; to get shift states
- Test AL,04 ; See if Ctrl has been pressed
- Jnz NormalProcess ; Process normally if Break
-
- Call VideoCheck ; See if video is OK for recall
- Jc NormalProcess ; If not, ignore the key
-
- Mov CS:[LockStateOn],1 ; Lock state now ON
- Push CS:[SavePointer] ; Transfer value of SavePointer
- Pop CS:[RecallPointer] ; ... to RecallPointer
- PushF ; Simulate an interrupt
- Call CS:[OldInterrupt9] ; Let Scroll Lock Register
- Call VideoOn ; Video might be off - turn on
-
- HoldingPattern: Cmp CS:[LockStateOn],1 ; Wait in this loop ...
- Jz HoldingPattern ; ... until out of lock state
-
- Jmp NormalProcess ; Then process last key
-
- ; Scroll Lock key released
- ; ------------------------
-
- ScrollLockRlse: Mov CS:[ScrollLockDown],0 ; Register Scroll Lock release
- Cmp CS:[LockStateOn],1 ; See if in lock state
- Jnz NormalProcess ; If not, just process key
-
- Mov AX,CS:[SavePointer] ; Check if the screen is in
- Cmp AX,CS:[RecallPointer] ; normal position
- Jnz NormalProcess ; If not, just process key
-
- Mov CS:[LockStateOn],0 ; If so, turn lock state OFF
- Pop AX ; Restore register
- IRet ; Return to holding pattern
-
- ; All other keys
- ; --------------
-
- AnyOtherKey: Cmp CS:[LockStateOn],1 ; For other keys, just do
- Jnz NormalProcess ; normal processing if not lock
-
- Call NowInLockState ; But call routine if locked
- Pop AX ; Restore register
- IRet ; Return to holding pattern
-
- NormalProcess: Pop AX ; Get back the register
- Jmp CS:[OldInterrupt9] ; Let the key process
-
- ; Routine for other keys during lock state (AL = Scan Code)
- ; ---------------------------------------------------------
-
- NowInLockState: Cld ; String moves generally forward
- Push CX ; We use all these registers
- Push DX
- Push SI
- Push DI
- Push DS
- Push ES
-
- Mov DX,CS ; Get value of CS
- Mov DS,DX ; Set DS and
- Mov ES,DX ; ES to this segment
- Assume DS:CSEG, ES:CSEG ; And tell the assembler
-
- Mov AH,AL ; AH is actual scan code
- And AL,7Fh ; AL has release byte off
-
- Sub AL,71 ; Subtract the 'Home' key value
- Jb WrongKey ; No good if below
- Cmp AL,(81 - 71) ; Above the 'PgDn' key?
- Ja WrongKey ; You're not right either
- Cmp AL,(73 - 71) ; Check if under or = 'PgUp'
- Jbe AllRightKey ; Ok for this key
- Cmp AL,(79 - 71) ; Check if over or = 'End'
- Jae AllRightKey ; You pass too
-
- WrongKey: Cmp [ScrollLockDown],1 ; See if scroll lock is down
- Jz ResetKeyboard ; If so, just ignore the key
-
- Call End ; If not, restore the display
- Mov [LockStateOn],0 ; Turn off the lock state
- Jmp KeyReturn ; And get out quickly
-
- AllRightKey: Test AH,80h ; See if cursor key is release
- Jnz ResetKeyboard ; If so, ignore the key
-
- Cbw ; Convert scan code to word
- Add AX,AX ; Double it because word access
- Mov SI,AX ; Set SI to it
- Call [KeyDispatch + SI] ; And do the routine
-
- ResetKeyboard: In AL,61h ; These instructions
- Mov AH,AL ; reset the keyboard
- Or AL,80h
- Out 61h,AL
- Mov AL,AH
- Out 61h,AL
-
- Cli ; Disable interrupts
- Mov AL,20h ; to reset the interrupt
- Out 20h,AL ; controller
-
- KeyReturn: Pop ES ; Get back all the pushes
- Pop DS
- Pop DI
- Pop SI
- Pop DX
- Pop CX
- Ret ; Return to previous routine
-
- ; Routines to scan up through saved display
- ; -----------------------------------------
-
- Home: Mov CX,0FFFFh ; 'Home' key -- "infinite" lines up
- Jmp Short GoingUp ; Do it
-
- Up: Mov CX,1 ; 'Up' key -- 1 line up
- Jmp Short GoingUp ; Go to it
-
- PgUp: Mov CX,25 ; 'PgUp' key -- 25 lines (1 screen) up
-
- GoingUp: Mov AX,[RecallPointer] ; Points to the text source
- Cmp AX,[TopHoldAddr] ; Check if it's at the top
- Ja NotScanUpTop ; If not there, no problem
- Mov AX,[BotHoldAddr] ; Otherwise must wrap around
- NotScanUpTop: Sub AX,Cols * 2 ; Go back one line for recall
- Cmp AX,[SavePointer] ; Check if cycled through yet
- Je EndGoingUp ; If so, abort this routine
-
- Mov [RecallPointer],AX ; Save the new pointer value
- Push CX ; And save our counter
-
- Mov SI,2 * 24 * Cols ; Set source to bottom line
- Call DisplayToSwap ; Transfer it to SWAP area
-
- Mov SI,2 * 24 * Cols - 2 ; End of penultimate line
- Mov DI,2 * 25 * Cols - 2 ; End of last line
- Std ; Backwards string transfer
- Call ScrollDisplay ; Do the screen scroll
- Cld ; Direction back to forward
-
- Mov DI,0 ; Destination is top line
- Call HoldToDisplay ; Move line to display
- Call SwapToHold ; And SWAP line to hold
-
- Pop CX ; Get back the counter
- Loop GoingUp ; And do CX times
-
- Dummy: ; Should never get here
- EndGoingUp: Ret ; But this is the end
-
- ; Routines to scan down through saved display
- ; -------------------------------------------
-
- End: Mov CX,0FFFFh ; 'End' key -- infinite lines down
- Jmp Short GoingDown ; Do it
-
- Down: Mov CX,1 ; 'Down' key -- 1 line down
- Jmp Short GoingDown ; All ready
-
- PgDn: Mov CX,25 ; 'PgDn' key -- 25 lines down
-
- GoingDown: Mov AX,[RecallPointer] ; Check if all through
- Cmp AX,[SavePointer] ; by this comparison
- Je EndGoingDown ; If so, exit this thing
-
- Push CX ; Save the counter
-
- Mov SI,0 ; Set source to top line
- Call DisplayToSwap ; Transfer it to SWAP area
-
- Mov SI,2 * 80 ; Source is second line
- Mov DI,0 ; Destination is top line
- Call ScrollDisplay ; So we can scroll up display
-
- Mov DI,2 * 24 * Cols ; Destination is bottom line
- Call HoldToDisplay ; For the saved text
- Call SwapToHold ; Move original top line in
-
- Mov AX,[RecallPointer] ; Adjust the recall pointer
- Add AX,Cols * 2 ; By increasing by one line
- Cmp AX,[BotHoldAddr] ; See if wrap around
- Jb NotScanDownBot ; If not, skip a little
- Mov AX,[TopHoldAddr] ; Get the top address
- NotScanDownBot: Mov [RecallPointer],AX ; And set to new recall
-
- Pop CX ; Get back the counter
- Loop GoingDown ; Do this CX times
-
- EndGoingDown: Ret ; Go back to caller
-
- ; Display to Swap -- Save top or bottom line of display (SI) in SWAP area
- ; -----------------------------------------------------------------------
-
- DisplayToSwap: Push DS ; Save data segment
- Mov DS,[DisplaySegment] ; And set it to display
- Mov DI,Offset Swap ; This is the destination
- Mov CX,Cols ; Do one line
- Call VideoOff ; Turn off color video
- Rep Movsw ; Do the line move
- Pop DS ; Get back DS
- Ret ; And end
-
- ; Scroll Video Display -- SI is source, DI destination
- ; ----------------------------------------------------
-
- ScrollDisplay: Push DS ; Save both these segments
- Push ES
- Mov ES,[DisplaySegment] ; Set both to display
- Mov DS,[DisplaySegment]
- Mov CX,24 * Cols ; 24 lines must be moved
- Rep Movsw ; This line does it
- Pop ES ; Get back the segments
- Pop DS
- Ret ; And go back
-
- ; Hold To Display -- Move one saved line to video display (DI)
- ; ------------------------------------------------------------
-
- HoldToDisplay: Push ES ; Now save just ES
- Mov ES,[DisplaySegment] ; And set it to display
- Mov SI,[RecallPointer] ; This is the source
- Mov CX,Cols ; One line only
- Rep Movsw ; Do the transfer
- Call VideoOn ; Color display back on
- Pop ES ; Get back ES
- Ret ; And return
-
- ; Swap To Hold -- Completes the cycle
- ; -----------------------------------
-
- SwapToHold: Mov SI,Offset Swap ; Source is SWAP
- Mov DI,[RecallPointer] ; Destination in HoldArea
- Mov CX,Cols ; One line transfer
- Rep Movsw ; Do it
- Ret ; And return
-
- ; Video Check -- Returns CY flag is graphics or 40 column or not page 0
- ; ---------------------------------------------------------------------
-
- Assume DS:Nothing, ES:Nothing
-
- VideoCheck: Push AX
- Push BX
-
- Mov CS:[DisplaySegment],0B000h ; Set up for monochrome
-
- Mov AH,15 ; Get current video mode
- Int 10h ; through display interrupt
-
- Cmp AL,7 ; See if monochrome
- Jz VideoAOK ; If so, skip out OK
- Cmp AL,3 ; Check if it's graphics
- Ja VideoNoGood ; If so, can't do anything
- Cmp BH,0 ; Check if it's Page 0
- Jnz VideoNoGood ; Nope? No bad
- Cmp AH,Cols ; See if columns is right
- Jne VideoNoGood ; No -- dishonorable discharge
-
- Mov CS:[DisplaySegment],0B800h ; Change it to color
- VideoAOK: Clc ; Flag for no error
- Jmp Short VideoReturn ; Get out
- VideoNoGood: Stc ; Flag for error
- VideoReturn: Pop BX ; Get back register
- Pop AX
- Ret ; Now go back
-
- ; Video Off -- Turns off display for Color / Graphics
- ; ---------------------------------------------------
-
- VideoOff: Cmp CS:[DisplaySegment],0B800h ; Check if color
- Jnz NoColorTurnOff ; If not, punt
-
- Push AX ; Just use two registers
- Push DX
- Mov DX,3DAh ; Color / Graphics Status Port
-
- RetraceWait1: In AL,DX ; Get Color / Graphics Status
- Test AL,08 ; Check the vertical retrace
- Jnz RetraceWait1 ; Loop till it's over
-
- RetraceWait2: In AL,DX ; Check status again
- Test AL,08 ; In particular the retrace
- Jz RetraceWait2 ; Loop till it comes
-
- Mov DX,3D8h ; Video Mode Select
- Mov AL,25h ; Byte to turn off video
- Out DX,AL ; Turn it off
-
- Pop DX ; Retrieve registers from stack
- Pop AX
- NoColorTurnOff: Ret
-
- ; Video On -- Turns Color Display back on
- ; ---------------------------------------
-
- VideoOn: Cmp CS:[DisplaySegment],0B800h ; See if color
- Jnz NoColorTurnOn ; If not do nothing
-
- Push AX ; Save registers
- Push DX
-
- Mov DX,3D8h ; Video mode register port
- Mov AL,28h - (Cols EQ 80) ; i.e., 29h for 80 columns
- Out DX,AL ; Turn video back on
-
- Pop DX ; Get back registers
- Pop AX
- NoColorTurnOn: Ret ; And leave
-
- ; Initialization Procedure
- ; ------------------------
-
- Assume DS:CSEG, ES:CSEG
-
- HoldArea Label Word ; Storage of screens also
-
- Initialize: Mov AX,Cols * 25 * 2 ; Characters per screen
- Mul [NumberOfScreens] ; AX = bytes for HoldArea
- Add AX,Offset HoldArea ; Calculate bottom of HoldArea
- Mov [BotHoldAddr],AX ; Save the bottom address
- Mov DX,AX ; Last address for terminate
-
- Sub AX,AX ; Zero out AX
- Mov DS,AX ; To set DS to Vector Segment
-
- Cli ; No Interrupts now
-
- Les SI,DS:[9h * 4] ; Get Int 9 address
- Mov Word Ptr CS:[OldInterrupt9],SI ; Save offset
- Mov Word Ptr CS:[OldInterrupt9 + 2],ES ; Save segment
- Mov Word Ptr DS:[9h * 4],Offset NewInterrupt9 ; New offset
- Mov Word Ptr DS:[9h * 4 + 2],CS ; New segment
-
- Les SI,DS:[10h * 4] ; Get Int 10 address
- Mov Word Ptr CS:[OldInterrupt10],SI ; Save offset
- Mov Word Ptr CS:[OldInterrupt10 + 2],ES ; Save segment
- Mov Word Ptr DS:[10h * 4],Offset NewInterrupt10 ; New offset
- Mov Word Ptr DS:[10h * 4 + 2],CS ; New segment
-
- Sti ; Interrupts back on
-
- Int 27h ; Terminate and stay resident
-
- CSEG EndS
-
- End Entry ; End denotes Entry point